﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using RootfusHostView;
using System.Windows;
using System.Diagnostics;

namespace RootfusCoreComponents
{
    /// <summary>
    /// Represents an input pin for a particular surface item (which owns us).
    /// We connect that with another output pin -- but we might be one of two types of pins:
    /// either a infinite input or a regular input. We subclass. One has only a 1:1 connection,
    /// the other is a many:1 connection.
    /// </summary>
    public abstract class PinConnection : DependencyObject
    {
        /// <summary>
        /// Keep track of the pin that we represent for input.
        /// </summary>
        private PinDefinition _pin;

        /// <summary>
        /// Init with the pin we are actually going to hold. This is the
        /// pin on the item that owns this object.
        /// </summary>
        /// <param name="pin"></param>
        public PinConnection(PinDefinition pin)
        {
            _pin = pin;
        }

        /// <summary>
        /// Gets the name of this particular pin data is being sent to for this pin.
        /// </summary>
        public string Name { get { return _pin.Name; } }


        /// <summary>
        /// Returns what type of connetion this input pin is.
        /// </summary>
        public PinConnectionType ConnectionType { get { return _pin.ConnectionType; } }

        /// <summary>
        /// True if some item is feeding this pin some data.
        /// </summary>
        public abstract bool HasInputs
        {
            get;
        }

        /// <summary>
        /// Creates the correct type of pin
        /// </summary>
        /// <param name="pin"></param>
        /// <returns></returns>
        public static PinConnection Create(PinDefinition pin)
        {
            switch (pin.ConnectionType)
            {
                case PinConnectionType.PinInfinite:
                    return new PinConnectionInfinite(pin);
                case PinConnectionType.PinSingle:
                    return new PinConnectionSingle(pin);
                default:
                    throw new Exception("Unimplemented!");
            }
        }

        /// <summary>
        /// Returns true if this connection is being fed by this item.
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public abstract bool ConnectedTo(RootfusSurfaceItemWrapper item);

        /// <summary>
        /// Returns true if non of our input items is marked as being updated.
        /// </summary>
        public abstract bool NoInputsUpdated
        {
            get;
        }
    }

    /// <summary>
    /// Represents a 1:1 input pin connection.
    /// </summary>
    public class PinConnectionSingle : PinConnection
    {
        public PinConnectionSingle(PinDefinition pin)
            : base(pin)
        {
            PinSource = new IncomingPinConnectionInfo();
        }

        /// <summary>
        /// Get/Set the pin data that will supply this pin with its data!
        /// </summary>
        public IncomingPinConnectionInfo PinSource
        {
            get { return (IncomingPinConnectionInfo)GetValue(PinSourceProperty); }
            set { SetValue(PinSourceProperty, value); }
        }

        /// <summary>
        /// True if we really do point to something.
        /// </summary>
        public bool ValidConnection
        {
            get { return PinSource != null && PinSource.Item != null && PinSource.Pin != null; }
        }

        // Using a DependencyProperty as the backing store for PinSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PinSourceProperty =
            DependencyProperty.Register("PinSource", typeof(IncomingPinConnectionInfo), typeof(PinConnectionSingle), new UIPropertyMetadata(null));

        /// <summary>
        /// Are we connected to anyone here?
        /// </summary>
        public override bool HasInputs
        {
            get { return PinSource != null; }
        }

        /// <summary>
        /// Are we connected to this source?
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public override bool ConnectedTo(RootfusSurfaceItemWrapper item)
        {
            if (PinSource == null || PinSource.Item == null)
            {
                return false;
            }
            return PinSource.Item == item;
        }

        /// <summary>
        /// True if our input item has not yet been updated.
        /// </summary>
        public override bool NoInputsUpdated
        {
            get { return PinSource == null || PinSource.Item == null || !PinSource.Item.Updated; }
        }
    }

    /// <summary>
    /// Represents a many:1 input pin connection(s).
    /// </summary>
    public class PinConnectionInfinite : PinConnection
    {
        public PinConnectionInfinite(PinDefinition pin)
            : base(pin)
        {
            PinSources = new ObservableCollection<IncomingPinConnectionInfo>();
        }

        /// <summary>
        /// Get/Set the list of incoming pins that supply this one pin with data. The list is already created; you can
        /// just add/remove from it.
        /// </summary>
        public ObservableCollection<IncomingPinConnectionInfo> PinSources
        {
            get { return (ObservableCollection<IncomingPinConnectionInfo>)GetValue(PinSourcesProperty); }
            set { SetValue(PinSourcesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PinSources.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PinSourcesProperty =
            DependencyProperty.Register("PinSources",
            typeof(ObservableCollection<IncomingPinConnectionInfo>),
            typeof(PinConnectionInfinite),
            new UIPropertyMetadata(null));

        /// <summary>
        /// Do we have any input sources connected right now?
        /// </summary>
        public override bool HasInputs
        {
            get { return PinSources.Count > 0; }
        }

        /// <summary>
        /// Is this item connected to any of our sources?
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public override bool ConnectedTo(RootfusSurfaceItemWrapper item)
        {
            return PinSources.Any(x => x.Item == item);
        }

        /// <summary>
        /// Returns true only if no item that is connected to us has seen the update.
        /// </summary>
        public override bool NoInputsUpdated
        {
            get { return PinSources.All(x => !x.Item.Updated); }
        }
    }
}
